package com.hxy.sys.aop;

import com.alibaba.fastjson.JSON;
import com.hxy.base.annotation.SysLog;
import com.hxy.base.utils.HttpContextUtils;
import com.hxy.base.utils.IPUtils;
import com.hxy.sys.entity.SysLogEntity;
import com.hxy.sys.service.SysLogService;
import com.hxy.utils.ShiroUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;

/**
 * 类SysLogAspect的功能描述:
 * 系统日志，切面处理类
 * @auther hxy
 * @date 2017-08-25 16:13:10
 */
@Aspect
@Component
public class SysLogAspect {
	@Autowired
	private SysLogService sysLogService;

	//开启切面编程（通过配置织入@Aspectj切面   <aop:aspectj-autoproxy/>
	//类上使用 @Component 注解 把切面类加入到IOC容器中
	//类上使用 @Aspect 注解 使之成为切面类
	//用@Aspect注解方式来实现前置通知、返回通知、后置通知、异常通知、环绕通知。

	@Pointcut("@annotation(com.hxy.base.annotation.SysLog)")
	public void logPointCut() { 
		
	}
	
	@Before("logPointCut()")
	public void saveSysLog(JoinPoint joinPoint) {
		MethodSignature signature = (MethodSignature) joinPoint.getSignature();
		Method method = signature.getMethod();
		
		SysLogEntity sysLog = new SysLogEntity();
		SysLog syslog = method.getAnnotation(SysLog.class);
		if(syslog != null){
			//注解上的描述 
			sysLog.setOperation(syslog.value());
		}
		
		//请求的方法名
		String className = joinPoint.getTarget().getClass().getName();
		String methodName = signature.getName();
		sysLog.setMethod(className + "." + methodName + "()");
		
		//请求的参数
		Object[] args = joinPoint.getArgs();
		if(args.length>0){
			String params = JSON.toJSONString(args[0]);
			sysLog.setParams(params);
		}

		//获取request
		HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
		//设置IP地址
		sysLog.setIp(IPUtils.getIpAddr(request));
		
		//用户名
		String username = ShiroUtils.getUserEntity().getUserName();

		sysLog.setUsername(username);
		
		sysLog.setCreateDate(new Date());
		//保存系统日志
		sysLogService.save(sysLog);
	}









	/**
	 * 前置通知：目标方法执行之前执行以下方法体的内容
	 * @param jp
	 */
	@Before("execution(* com.hxy.sys.controller.*.*(..))")
	public void beforeMethod(JoinPoint jp){
		String methodName = jp.getSignature().getName();
		System.out.println("【1beforeMethod前置通知】the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs()));
	}

	/**
	 * 后置通知：目标方法执行之后执行以下方法体的内容，不管是否发生异常。
	 * @param jp
	 */
	@After("execution(* com.hxy.sys.controller.*.*(..))")
	public void afterMethod(JoinPoint jp){
		System.out.println("【3afterMethod后置通知】this is a afterMethod advice...");
	}

	/**
	 * 返回通知：目标方法正常执行完毕时执行以下代码
	 * @param jp
	 * @param result
	 */
	@AfterReturning(value="execution(* com.hxy.sys.controller.*.*(..))",returning="result")
	public void afterReturningMethod(JoinPoint jp, Object result){
		String methodName = jp.getSignature().getName();
		System.out.println("【2afterReturningMethod返回通知】the method 【" + methodName + "】 ends with 【" + result + "】");
	}

	/**
	 * 异常通知：目标方法发生异常的时候执行以下代码
	 */
	@AfterThrowing(value="execution(* com.hxy.sys.controller.*.*(..))",throwing="e")
	public void afterThorwingMethod(JoinPoint jp, NullPointerException e){
		String methodName = jp.getSignature().getName();
		System.out.println("【4afterThorwingMethod异常通知】the method 【" + methodName + "】 occurs exception: " + e);
	}

	  /**
	   * 环绕通知：目标方法执行前后分别执行一些代码，发生异常的时候执行另外一些代码
	   * @return
	   */
	  @Around(value="execution(* com.hxy.sys.controller.*.*(..))")
	  public Object aroundMethod(ProceedingJoinPoint jp){
		  String methodName = jp.getSignature().getName();
		  Object result = null;
		  System.out.println("【11beforeMethod环绕通知中的--->前置通知】：the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs()));
		  try {
			  //执行目标方法
			  result = jp.proceed();
			  System.out.println("【22afterReturningMethod环绕通知中的--->返回通知】：the method 【" + methodName + "】 ends with " + result);
		  } catch (Throwable e) {
			  System.out.println("【44afterThorwingMethod环绕通知中的--->异常通知】：the method 【" + methodName + "】 occurs exception " + e);
		  }

		  System.out.println("【33afterMethod环绕通知中的--->后置通知】：-----------------end.----------------------");
		  return result;
	  }

}
